/* 
 * File:   ThreadMngRfidCard.cpp
 * Author: batista
 * 
 * Created on 17 de Março de 2013, 17:45
 */

#include "ThreadMngRfidCard.h"

ThreadMngRfidCard::ThreadMngRfidCard() : JoinableThread() {
    //    
    tmut = new TimeUtil();

}

ThreadMngRfidCard::~ThreadMngRfidCard() {
}

void ThreadMngRfidCard::run() {
    vector<Card*> vec_eventcard_to_work;
    //
    ShowMessage("ThreadMngRfidCard()", COLOR_PINK, COLOR_GREEN);
    //
    ::sleep(5);
    /* Pega o time-out para os atendimentos dos eventos */
    timeout_attend = dbconn->getTimeOutForAttend();
    //
    //Loop infinito
    while (1) {
        //
        ShowMessage("(  PROCURANDO CARTOES PARA APLICAR REGRAS )", COLOR_BLUE, COLOR_PINK);

        //Aguarda 100 milessimo de segundo
        tmut->SleepSeg(100);
        //

        //Pega eventos disponiveis para trabalho
        vec_eventcard_to_work = dbconn->getCardToWorkFromSqLite();

        /**
         * Se tiver eventos disponiveis na lista, 
         * entra para aplicar a regra de negocio. 
         */
        if (!vec_eventcard_to_work.empty()) {
            card = new Card();
            //
            for (int i = 0; i < vec_eventcard_to_work.size(); i++) {
                card = vec_eventcard_to_work.at(i);

                /**
                 * Desmembra e seta os dados para 
                 * o cartão.
                 */
                card = setCardToApplyRule(card);

                /**
                 * Se nao conseguir pegar o codigo
                 * do cartao! nao faz as outras 
                 * pesquisas.
                 */
                if (!card->GetCode_Card().empty() && !card->GetRoomCode().empty()) {
                    /**
                     * Pega o ID do Cartao para tabela local
                     */
                    card = dbconn->getCardIDToCardInSqLite(card);
                }

                /**
                 * So Entra no if quando ja tiver um ID para 
                 * o Cartao, Se nao vai para else para 
                 * adicionar um novo Cartao para o PostgreSQL, 
                 * caso nao tenha ID
                 */
                if (!card->GetAttendant_Id().empty()) {

                    /**
                     * Metodo responsavel por pegar o ID da Ala para o
                     * IP do Cartao, na tabela local
                     */
                    card = dbconn->getWardIDToWardCardInSqLite(card);

                    /**
                     * Se a Ala teum ID, entra para pegar o
                     * ID do Quarto
                     */
                    if (!card->GetWardId().empty()) {
                        /**
                         * Metodo responsavel por pegar ID do Quarto
                         * para o Leitor do Cartao na tabela local
                         */
                        card = dbconn->getRoomIDToRoomCardInSqLite(card);
                        //

                        /**
                         * Se nao encontrou o quarto...!
                         * descarta o evento
                         */
                        if (card->GetRoomId().empty()) {
                            /**
                             * Descarta o evento quando nao 
                             * encontra o id do quarto para ala
                             * do cartao. 
                             * Remove da tabela event_card em
                             * SqLite.
                             */
                            dbconn->runRemoveEventInSqLite(card);
                        }

                    }

                    /**
                     * Se tem o ID do Quarto..Entao pode
                     * aplicar a regra de negocio.
                     */
                    if (!card->GetRoomId().empty() && !card->GetAttendant_Id().empty()) {
                        /**
                         * Metodo responsavel por aplicar
                         * as regras de negocios
                         */
                        runApplyRulesForRfidCard(card);
                    }

                } else if (card->GetCode_Card().empty() && card->GetRoomCode().empty()) {
                    /**
                     * Descarta o evento quando nao 
                     * tiver o codigo do quarto e codigo
                     * do cartao. 
                     * Remove da tabela event_card em
                     * SqLite.
                     */
                    dbconn->runRemoveEventInSqLite(card);
                    //
                    std::string erro_msg = "\nNAO FOI POSSIVEL TRADUZIR OS DADOS PAR O CARTAO:\n " + card->GetHexa_Card() +
                            "OS DADOS ESTAO SENDO DESCARTADOS...!";
                    //Pasta onde serar gravado o arquivo
                    dirfl->setFolder_write("/Logs/Card/Erro/");
                    //     
                    //
                    dirfl->CreateLogFile("LOGCARDERRO", erro_msg);

                    //Aguarda um segundo
                    //tmut->SleepSeg(100);
                }
            }//fim for
        }//fim if

    }//fim while
    //    
}

/**
 * Metodo Responsavel por desmembrar os
 * dados para o cartão e setar os dados
 * para ser aplicada a regra de negócio.
 * @param ncard
 * @return 
 */
Card* ThreadMngRfidCard::setCardToApplyRule(Card *ncard) {
    std::string hexa_card, code_card, room_code, hexa_temp, msg;
    //
    hexa_temp = ncard->GetHexa_Card();

    /**
     * Remove o delimitador (#)
     */
    hexa_temp = hexa_temp.erase(0, 1); //Remove (#)

    /**
     * Remove o delimitador (@)
     */
    hexa_temp = hexa_temp.erase(hexa_temp.length() - 1, 1); //Remove (@)

    /**
     * Pega o codigo do quarto do caracter
     * ascii da posicao (0), transforma em
     * inteiro string.
     */
    room_code = tmut->convCharToLong(hexa_temp[0]);

    //Remove o char ascii da possicao (0)
    hexa_temp = hexa_temp.erase(0, 1);

    /**
     * Ordena os dados do Cartao em hexa
     * para o formato correto.
     * @param ncard
     * @return 
     */
    hexa_temp = card->reOrderHexaCard(hexa_temp);

    //Guarda o hexa organizado na variavel
    hexa_card = hexa_temp;

    //Pega o cogido do cartao em numerico
    code_card = tmut->StringHexToStringInteger(hexa_temp);

    //Seta os novos dados
    ncard->SetHexa_Card(hexa_card);

    //Seta o codigo do quarto
    ncard->SetRoomCode(room_code);

    //Seta o codigo do Cartao
    ncard->SetCode_Card(code_card);

    /**
     * Se conseguiu pegar o quarto e o
     * codigo do quarto.. entra para 
     * tratar os dados.
     * @param ncard
     * @return 
     */
    if (!code_card.empty() && !room_code.empty()) {
        //Gravar os dados para arquivo
        msg = "\n-------------------------------------------------\n"
                "\nAla...........: " + ncard->GetWardPds() +
                "\nCódigo Quarto.: " + ncard->GetRoomCode() +
                "\nCódigo em Hexa: " + ncard->GetHexa_Card() +
                "\nCódigo Cartão.: " + ncard->GetCode_Card() +
                "\nHora Atual....: " + tmut->getTimeRfc2822() +
                "\n-------------------------------------------------\n";
        //
        ShowMessage(msg, COLOR_BLUE, COLOR_PINK);
        //Pasta onde serar gravado o arquivo
        dirfl->setFolder_write("/Logs/Card/Add/");
        //     
        //
        dirfl->CreateLogFile("LOGCARDOK", msg);

        //Aguarda um segundo
        tmut->SleepSeg(100);
    } else {
        //Pasta onde serar gravado o arquivo
        dirfl->setFolder_write("/Logs/Card/Erro/");
        //     
        //
        dirfl->CreateLogFile("LOGCARDERRO", msg);

        //Aguarda um segundo
        tmut->SleepSeg(100);
    }
    // 
    return ncard;
}

/**
 * Metodo responsavel por aplicar
 * a regra de negocio para o cartao
 * e adicionar na base de dados.
 */
void ThreadMngRfidCard::runApplyRulesForRfidCard(Card *ncard) {
    std::string date_with_timeout, query, msg;
    //
    card = new Card();

    /**
     * Pesquisa se o evento existe para ser 
     * atendido ou finalizado
     * @param event_card
     */
    card = dbconn->getTypeCardEventToApplyRule(ncard);

    //pmsg.PrintMessage("Em AddInListEvents(), Room_code: " + evcard.getRoom_code() + " Card_code: " + evcard.getCard_code(), COLOR_MAGENTA, COLOR_BLUE);

    switch (getOptionForApply(card)) {
            /**
             * Se evente_rauland está com um id e is_cancel for igual a true, seguinifica que há um evento
             * para ser atendido, então etende
             * @param event_rauland
             */
        case 1:// 01 - ATENDIMENTO 
            ShowMessage("ATENDE UM EVENTO!", COLOR_BLUE, COLOR_PINK);
            /**
             * Aqui valida se o atendimento está sendo feito dentro do tempo
             * informado, caso nao esteja, adiciona com time-out
             * @param event_card
             */
            if (tmut->ValidAttendTime(card->GetDate_Time_Called(), getTimeout_attend()) == true) {
                //Pega a data com time-out
                date_with_timeout = tmut->SumTimeOut(card->GetDate_Time_Called(), getTimeout_attend());

                //Monta a query com time-out
                query = "UPDATE data_protocol SET attend_id  = '" + card->GetAttendant_Id() +
                        "', date_time_entered = '" + date_with_timeout +
                        "', status_entered = 't' WHERE id = '" + tmut->ConvIntToString(card->GetPtlId()) + "'";

            } else {
                //Monta query sem time-out
                query = "UPDATE data_protocol SET attend_id = '" + card->GetAttendant_Id() +
                        "', date_time_entered = '" + card->GetDate_Time_Called()
                        + "', status_entered = 't' WHERE id = '" + tmut->ConvIntToString(card->GetPtlId()) + "'";
            }

            //Atualiza na base de dados
            if (dbconn->runQueryInPGree(query)) {
                /**
                 * Apos a inclusao com sucesso!
                 * Remove da tabela event_card em
                 * SqLite.
                 */
                dbconn->runRemoveEventInSqLite(card);
            }
            //
            break;
        case 2:// 02 - COLOCA STATUS DE ATENDIMENTO 
            ShowMessage("COLOCA STATUS DE ATENDIMENTO!", COLOR_BLUE, COLOR_PINK);

            //Monta query para colocar status de atendido
            query = "UPDATE data_protocol SET attend_id = '" + card->GetAttendant_Id() +
                    "',  status_entered = 't' WHERE id = '" + tmut->ConvIntToString(card->GetPtlId()) + "'";

            //Atualiza na base de dados
            if (dbconn->runQueryInPGree(query)) {
                /**
                 * Apos a inclusao com sucesso!
                 * Remove da tabela event_card em
                 * SqLite.
                 */
                dbconn->runRemoveEventInSqLite(card);
            }
            //
            break;

            /**
             * Se evente_rauland está com atendimento porem não foi finalizado,
             * finaliza o evento com ou sem time-out e gera um novo.
             * @param event_cards
             */
        case 3: // 03 - FINALIZA ATENDIMENTO
            ShowMessage("FINALIZA ATENDIMENTO!", COLOR_BLUE, COLOR_PINK);
            /**
             * Aqui valida se esta finalizando o evento dentro do tempo
             * informado, caso nao seja, finaliza com time-out
             * @param event_cards
             */
            if (tmut->ValidAttendTime(card->GetDate_Time_Called(), getTimeout_attend()) == true) {
                //Pega a data com time-out
                date_with_timeout = tmut->SumTimeOut(card->GetDate_Time_Called(), getTimeout_attend());

                //Monta query com time-out
                query = "UPDATE data_protocol SET attend_id = '" + card->GetAttendant_Id() +
                        "', date_time_exited = '" + date_with_timeout
                        + "', status_exited = 't' WHERE id  = '" + tmut->ConvIntToString(card->GetPtlId()) + "'";
            } else {
                //Monta query sem time-out
                query = "UPDATE data_protocol SET attend_id = '" + card->GetAttendant_Id() +
                        "', date_time_exited = '" + card->GetDate_Time_Called()
                        + "', status_exited = 't' WHERE id  = '" + tmut->ConvIntToString(card->GetPtlId()) + "'";

            }

            //Atualiza na base de dados
            if (dbconn->runQueryInPGree(query)) {
                /**
                 * Apos a inclusao com sucesso!
                 * Remove da tabela event_card em
                 * SqLite.
                 */
                dbconn->runRemoveEventInSqLite(card);
            }
            //
            break;
        case 4:
            //
            //Pega o ID para o evento de Atendimento Espontaneo
            card = dbconn->getEventIDToAEInPGree(card);
            /**
             * Se encontra o ID para o evento
             * entra para adicionar o evento.
             * Caso contrario descarta ele
             * @param ncard
             */
            if (!card->GetEventId().empty()) {
                //
                ShowMessage("ADD UM NOVO EVENTO COMO ATENDIMENTO ESPONTANEO!", COLOR_BLUE, COLOR_PINK);
                //
                query = "INSERT INTO data_protocol (ward_id, room_id, event_id, date_time_called, "
                        "bed_room_code, attend_id, date_time_entered, status_entered) VALUES ('"
                        + card->GetWardId() + "','" + card->GetRoomId() + "','"
                        + card->GetEventId() + "','" + card->GetDate_Time_Called() + "','"
                        + card->GetRoomBedCode() + "','" + card->GetAttendant_Id() + "','"
                        + card->GetDate_Time_Called() + "','t')";

                //Adiciona na base de dados
                if (dbconn->runQueryInPGree(query)) {
                    //Remove o evento da tabela SqLite
                    if (!dbconn->runRemoveEventInSqLite(card))
                        ShowMessage("NAO FOI POSSIVEL REMOVER EVENTO TRATADO!!!", COLOR_BLUE, COLOR_RED);
                }
            } else {
                /**
                 * Descarta o evento quando nao 
                 * encontra o ID para o evento de 
                 * atendimento espontaneo.
                 * Remove da tabela event_card em
                 * SqLite.
                 */
                dbconn->runRemoveEventInSqLite(card);
            }
            //            
            break;
        default://se o evento for maior que 40, entra aqui
            break;
    }


}

/**
 * Metodo responsavel por pegar a opção
 * para aplicar para o evento do Cartao.
 * @param ncard
 * @return 
 */
int ThreadMngRfidCard::getOptionForApply(Card *ncard) {
    std::string msg;

    /**
     * Só faz a validação se o evento tiver um id.
     * @param event_card
     * @return 
     */
    if (ncard->GetPtlId() > 0) {
        /**
         * Se a data de entrada e de saída estiver vazia,
         * faz o atendimento.
         * @param event_card
         */
        if (ncard->GetDate_Time_Entered().empty() &&
                ncard->GetDate_Time_Exited().empty()) {
            //
            ShowMessage("ATENDE UM EVENTO!", COLOR_BLUE, COLOR_PINK);
            //
            return 1;
        }


        /**
         * Se já tiver atendimento e status_entered for igual a false,
         * atualiza o status e o atendente para o evento.
         * @param event_card
         */
        if (!ncard->GetDate_Time_Entered().empty() && ncard->IsEventEntered() == false) {
            //
            ShowMessage("ATUALIZA STATUS PARA EVENTO ATENDIDO!", COLOR_BLUE, COLOR_PINK);
            //
            return 2;
        }


        /**
         * Se evente_rauland não foi finalizado, finaliza o evento 
         * com ou sem time-out.
         * @param event_rauland
         */
        if (ncard->GetDate_Time_Exited().empty() && ncard->IsEventExited() == false) {
            //
            ShowMessage("FINALIZA UM EVENTO E ATUALIZA O STATUS!", COLOR_BLUE, COLOR_PINK);
            //
            return 3;
        }


    } else {
        //
        ShowMessage("GERA UM EVENTO COMO ATENDIMENTO ESPONTANEO!", COLOR_BLUE, COLOR_PINK);
        //
        return 4;
    }
}


